﻿using Elio.Controls;
using Elio.Interfaces;
using Google.Protobuf.WellKnownTypes;

namespace Elio.Sessions.Widgets;

public abstract class BaseWidgetSession<TV> : BaseSession where TV : View, ISessionWidget
{
    // ReSharper disable once StaticMemberInGenericType
    private static readonly SemaphoreSlim Semaphore = new(1);
    protected abstract TV? View { get; }

    protected override async Task OnCreate(long sid = 0, long fid = 0, params Any[] argv)
    {
        ParseOptions(out InitOptions options, argv);
        if (View is not null)
        {
            View.Init(options);
            Parent
                ?.ElegantPage
                ?.Views.Add(View);
            View.Session = this;
        }
        await base.OnCreate(sid, fid, argv);
    }

    private static void ParseOptions(out InitOptions options, params Any[] argv)
    {
        options = new InitOptions
        {
            Width = argv[0].P2Int(),
            Height = argv[1].P2Int(),
            X = argv[2].P2Int(),
            Y = argv[3].P2Int(),
        };

        if (argv[4].TryUnpack(out StringValue text)) options.Text = text.Value;
        if (argv[4].TryUnpack(out BoolValue b)) options.IsModel = b.Value;
    }

    protected override async Task OnDestroy()
    {
        // 不采用信号控制，控件会出现残留
        await Semaphore.WaitAsync();
        if (View is not null)
            Parent
                ?.ElegantPage
                ?.Views
                .Remove(View);
        Semaphore.Release();
        await base.OnDestroy();
    }

    protected override async Task OnReceive(string name, long sid, long fid, params Any[] argv)
    {
        // 把消息转发到控件
        if (View is not null && await View.OnViewReceive(name, sid, fid, argv))
        {
            return;
        }

        switch (name)
        {
            case "getAccessibilityLabel":
                await GetAccessibilityLabel(sid, fid);
                break;

            case "getRegion":
                await GetRegion(sid, fid);
                break;

            case "isEnabled":
                await IsEnabled(sid, fid);
                break;

            case "click":
                Click();
                break;

            case "focus":
                Focus();
                break;

            case "setAccessibilityLabel":
                SetAccessibilityLabel(argv);
                break;

            case "setLocation":
                SetLocation(argv);
                break;

            case "setBackgroundColor":
                SetBackgroundColor(argv);
                break;

            case "setOnClickListener":
                SetOnClickListener();
                break;

            case "setOnLongClickListener":
                SetOnLongClickListener();
                break;

            case "setOnGestureListener":
                SetOnGestureListener();
                break;

            case "setEnabled":
                SetEnabled(argv);
                break;

            default:
                await base.OnReceive(name, sid, fid, argv);
                break;
        }
    }

    private void Click() => View?.SendClicked();

    private void Focus() => View?.SendFocused();

    private async Task GetAccessibilityLabel(long sid, long fid)
    {
        string info = SemanticProperties.GetDescription(View) ?? string.Empty;
        await SendAction("onGetAccessibilityLabel", sid, fid, info.ToAny());
    }

    private void SetAccessibilityLabel(params Any[] argv) =>
            SemanticProperties.SetDescription(View, argv[0].P2String());

    private void SetLocation(params Any[] argv)
    {
        if (View is null) return;
        (int x, int y) = (argv[0].P2Int(), argv[1].P2Int());
        Rect rect = new(x, y, View.WidthRequest, View.HeightRequest);
        AbsoluteLayout.SetLayoutBounds(View, rect);
    }

    private void SetBackgroundColor(params Any[] argv)
    {
        if (View is null) return;
#pragma warning disable CS0618 // 类型或成员已过时
        View.Background = Color.FromHex(argv[0].P2String());
#pragma warning restore CS0618 // 类型或成员已过时
    }

    private async Task GetRegion(long sid, long fid)
    {
        Point point = (View is null)
            ? new Point()
            : CustomAbsoluteLayout.GetPoint(View);
        (int x, int y) = ((int)point.X, (int)point.Y);
        int width = (int)(View?.WidthRequest ?? 0);
        int height = (int)(View?.HeightRequest ?? 0);
        await SendAction("onGetRegion", sid, fid, x.ToAny(), y.ToAny(), width.ToAny(), height.ToAny());
    }

    private static void SetOnClickListener() { }

    private static void SetOnLongClickListener() { }

    private static void SetOnGestureListener() { }
    private async Task IsEnabled(long sid, long fid) =>
        await SendAction("onIsEnabled", sid, fid, (View?.IsEnabled ?? false).ToAny());

    private void SetEnabled(params Any[] argv)
    {
        if (View is null) return;
        View.IsEnabled = argv[0].P2Bool();
    }
}

public record InitOptions
{
    public int Width { get; init; }
    public int Height { get; init; }
    public int X { get; init; }
    public int Y { get; init; }
    public string Text { get; set; } = string.Empty;
    public bool IsModel { get; set; }
}